from rest_framework import status, mixins
from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.generics import GenericAPIView
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination
from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.throttling import AnonRateThrottle
from rest_framework.viewsets import ModelViewSet, GenericViewSet

from booktest.models import BookInfo
from .serializers import BookInfoSerializer


# class MyListModelMixin(object):
#     def list(self,request,*args,**kwargs):
#         # 查询出所有图书信息
#         queryset = self.get_queryset()
#         # 创建序列化器对象并将所有图书信息存入序列化器对象中
#         serializer = self.get_serializer(queryset, many=True)
#         # 返回json给前端,response可以将Python对象自动转成json或者html,
#         # 具体根据前端发来的accept类型发送，默认发送json
#         return Response(serializer.data)
#
# class MyCreateModelMixin(object):
#     def create(self,request,*args,**kwargs):
#         # 获取参数并进行参数校验
#         serializer = self.get_serializer(data=request.data)
#         # 反序列化-数据校验
#         serializer.is_valid(raise_exception=True)
#         # 反序列化-数据保存
#         serializer.save()
#         # 返回数据给前端，并把新增数据成功状态码发送201
#         return Response(serializer.data, status=status.HTTP_201_CREATED)
#
# """自定义Mixin扩展类"""
# """先定义一个图书类视图，继承GenericAPIView"""
# # 定义一个类视图,可以说是一个 视图集
# class BookListView(MyListModelMixin,
#                    MyCreateModelMixin,
#                    GenericAPIView):
#     #指定当前视图所使用的查询集
#     queryset = BookInfo.objects.all()
#     #指定当前视图所使用的序列化器类
#     serializer_class = BookInfoSerializer
#
#     """获取所有图书的信息"""
#     def get(self,request):
#         #查询出所有图书信息
#         #创建序列化器对象并将所有图书信息存入序列化器对象中
#         #返回json给前端
#         return self.list(request)
#
#
#     """新增一本图书信息"""
#     def post(self,request):
#         #需要客户端传递新增的图书信息（btitle,bpub_date),通过json传递
#         #获取参数并进行参数校验
#         #反序列化-数据校验
#         #反序列化-数据保存
#         #返回数据给前端，并把新增数据成功状态码发送201
#         return self.create(request)
#
#
# class MyRetrieveModelMixin(object):
#     def retrieve(self,request,*args,**kwargs):
#         # 根据pk主键查询指定的图书,,get_object方法里也是根据主键进行查找的
#         obj = self.get_object()
#         # 创建序列化器对象并将所有图书信息存入序列化器对象中
#         serializer = self.get_serializer(obj)
#         # 返回数据给前端
#         return Response(serializer.data)
#
# class MyUpdateModelMixin(object):
#     def update(self,request,*args,**kwargs):
#         # 根据主键pk查询指定的图书
#         obj = self.get_object()
#         # 创建序列化器对象并将所有图书信息存入序列化器对象中,
#         # request.data可以获取
#         serializer = self.get_serializer(obj, data=request.data)
#         # 反序列化-数据校验
#         serializer.is_valid(raise_exception=True)
#         # 反序列化-数据更新保存
#         serializer.save()
#         # 返回数据给前端
#         return Response(serializer.data)
#
# class MyDestroyModelMixin(object):
#     def destroy(self,request,*args,**kwargs):
#         # 根据主键pk查询指定的图书
#         obj = self.get_object()
#         # 直接删除指定图书
#         obj.delete()
#         # 返回空字符串和删除成功204状态码
#         return Response(status=status.HTTP_204_NO_CONTENT)
#
#
# """创建一个图书详情类视图"""
# class BookDetailView(MyRetrieveModelMixin,
#                      MyUpdateModelMixin,
#                      MyDestroyModelMixin,
#                      GenericAPIView):
#     #指定当前视图所使用的查询集
#     queryset = BookInfo.objects.all()
#     #指定当前视图所使用的序列化器类
#     serializer_class = BookInfoSerializer
#
#     """获取指定的图书信息"""
#     def get(self,request,pk):
#         #根据pk主键查询指定的图书
#         #创建序列化器对象并将所有图书信息存入序列化器对象中
#         #返回数据给前端
#         return self.retrieve(request,pk)
#
#     """修改指定的图书"""
#     def put(self,request,pk):
#         #根据主键pk查询指定的图书
#         #获取参数
#         #反序列化-数据校验
#         #反序列化-数据更新保存
#         #返回数据给前端
#
#         return self.update(request,pk)
#
#     """删除指定图书"""
#     def delete(self,request,pk):
#         #根据主键pk查询指定的图书
#         #直接删除指定图书
#         #返回空字符串和删除成功204状态码
#
#         return self.destroy(request,pk)

"""GenericViewSet是需要配合mixin扩展类使用的，本身只提供查询集和数据库的操作"""
#只要继承了这五个mixin扩展类，下面五个接口都可以实现
# 1. 获取所有图书的信息 GET /books/
# 2. 新增一本图书信息	 POST /books/
# 3. 获取指定的图书信息 GET /books/(?P<pk>\d+)/
# 4. 修改指定的图书信息 PUT /books/(?P<pk>\d+)/
# 5. 删除指定的图书信息 DELETE /books/(?P<pk>\d+)/
# class BookInfoViewSet(mixins.ListModelMixin,
#                       mixins.CreateModelMixin,
#                       mixins.RetrieveModelMixin,
#
#                   mixins.UpdateModelMixin,
#                       mixins.DestroyModelMixin,
#                       GenericViewSet):
#     queryset = BookInfo.objects.all()
#     serializer_class = BookInfoSerializer

#自定义分页，必须继承PageNumberPagination
class StandardResultPagination(PageNumberPagination):
    # 默认页容量，可以自己设置
    page_size = 3

    # 指定获取分页数据时，传递页容量的参数名字，就是在URL用？后面拼接的参数名
    page_size_query_param = 'page_size'

    # 指定最大页容量，就是一页最多显示多少条
    max_page_size = 5


#自定义DRF权限控制类,必须继承BasePermission
class MyPermission(BasePermission):
    def has_permission(self, request, view):
        #判断对当前视图是否有访问权限，返回True代表有权限，返回False代表没权限
        return True
    def has_object_permission(self, request, view, obj):
        #判断对某个数据对象是否有访问权限，返回True代表有权限，返回False代表没权限
        #view是使用此权限控制类的视图集，obj是查询集中的模型对象
        #需求：id为1,3数据对象可以进行访问，其他数据对象不允许访问
        if obj.id in (1,3):
            return True
        return False

"""直接继承ModelViewSet就可以有5个接口都能实现，不需继承扩展类了"""
class BookInfoViewSet(ModelViewSet):
    """视图集"""
    #继承了ModelViewSet后，就可以实现5个接口
    #只需要指定视图集使用的查询集和序列器
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer


    #指明使用Router动态生成视图集的url配置项时提取参数正则表达式
    lookup_value_regex = '\d+'

    # 指定当前视图所使用的认证方式(一旦指定，不再使用默认全局认证方式，默认的有BasicAuthentication和SessionAuthentication两种)

    authentication_classes = (SessionAuthentication,)

    #指定当前视图所使用的权限控制方式（一旦指定，不再使用默认全局权限控制方式）
    #permission_classes = (IsAuthenticated)---默认全局权限控制,是允许所有访问

    #用自定义的权限控制方式,要填一个元祖，
    permission_classes = (MyPermission,)

    #指定当前视图所采用的限流类（这里是只针对匿名用户进行限流）
    throttle_classes = (AnonRateThrottle,)
    #指定当前视图所采用的限流方案,就是在setting配置文件中自己起的名字，
    throttle_scope = 'upload'

    # 排序
    filter_backends = (OrderingFilter,)
    # 指定排序字段,就是按什么字段排序，这里是先id排序，id相同的情况按日期以此类推，默认是从小到大排序，如果要降序可以在URL参数前加个‘-’号
    ordering_fields = ('id', 'bpub_date', 'bread')


    #指定过滤字段,需要去setting文件中配置
    filter_fields = ('bread','btitle')

    # 使用此分页类，获取分页数据时可以传递2个参数：page=<页码>&page_size=<页容量>
    pagination_class = StandardResultPagination
    # 如果想要关闭分页当前视图关闭分页
    # pagination_class = None

    # 还可以使用此分页类，获取分页数据时可以传递2个参数：offset=<偏移量>&limit=<取几条>
    # pagination_class = LimitOffsetPagination


    #自定义方法，也要自己配路由
    #GET /books/latest/
    """获取最新发布的图书信息"""
    #自定义方法的如果要动态生成URL，就必须使用action装饰器
    # 要指明请求方式和请求路径，和url_name，
    # 也要指明detail的值，为False时，是不需要获取匹配正则命名的URL参数
    # 为True时,可以获取到，所以要看是否需要获取路由参数来决定detail的值
    # detail: 声明该action的路径是否与单一资源对应，及是否是xxx/<pk>/action方法名/
    # True 表示路径格式是xxx/<pk>/action装饰的方法名/
    # False 表示路径格式是xxx/action装饰的方法名/
    @action(methods=['GET'],detail=False,url_path='latest',url_name='latest')
    def latest(self,request):
        book = BookInfo.objects.latest('id')
        # 创建序列化器对象并将所有图书信息存入序列化器对象中,
        serializer = self.get_serializer(book)
        #返回数据给前端
        return Response(serializer.data)

    #PUT /books/(?P<pk>\d+)/read/
    """修改指定图书阅读量（只修改阅读量）"""
    @action(methods=['PUT'],detail=True)
    def read(self,request,pk):
        #根据pk主键查询指定的图书,,get_object方法里也是根据主键进行查找的
        #前端通过json传递修改阅读量bread
        #存入数据库
        #创建序列化器对象并将所有图书模型对象存入序列化器对象中
        #返回数据给前端


        #根据pk主键查询指定的图书,,get_object方法里也是根据主键进行查找的
        book = self.get_object()
        #前端通过json传递修改阅读量bread
        bread = request.data.get('bread')
        #存入数据库
        book.bread = bread
        book.save()
        #创建序列化器对象并将所有图书模型对象存入序列化器对象中
        serializer = self.get_serializer(book)
        #返回数据给前端
        return Response(serializer.data)
